package com.hr.checkrepetition.service.impl;

import com.hr.checkrepetition.component.CompletableThreadPoolExecutor;
import com.hr.checkrepetition.constants.RedisContants;
import com.hr.checkrepetition.enums.ArticleCheckPlatformEnum;
import com.hr.checkrepetition.enums.PostCheckStatusEnum;
import com.hr.checkrepetition.enums.ResponseStatusEnum;
import com.hr.checkrepetition.exceptions.BaseBizException;
import com.hr.checkrepetition.model.dto.ArticleCheckDto;
import com.hr.checkrepetition.model.dto.PostCheckInfoDto;
import com.hr.checkrepetition.model.dto.PostReportDto;
import com.hr.checkrepetition.model.entity.ArticleCheckResult;
import com.hr.checkrepetition.model.entity.ChannelPost;
import com.hr.checkrepetition.model.entity.PostCheck;
import com.hr.checkrepetition.model.entity.PostCheckScope;
import com.hr.checkrepetition.model.vo.ArticleCheckListVo;
import com.hr.checkrepetition.model.vo.ArticleCheckVo;
import com.hr.checkrepetition.service.ArticleDupCheckService;
import com.hr.checkrepetition.service.ChannelPostService;
import com.hr.checkrepetition.service.PostCheckScopeService;
import com.hr.checkrepetition.service.PostCheckService;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.welllan.utils.WlSimilarClassContent;
import com.welllan.utils.WlSimilarUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class ArticleDupCheckServiceImpl implements ArticleDupCheckService {

    private CompletableThreadPoolExecutor executor;
    private ChannelPostService channelPostService;
    private PostCheckService postCheckService;
    private PostCheckScopeService postCheckScopeService;
    private StringRedisTemplate stringRedisTemplate;
    private ObjectMapper objectMapper;


    @Override
    public void check(ArticleCheckDto dto, Integer userId) {
        if (CollectionUtils.isEmpty(dto.getPostIdList())) {
            throw new BaseBizException(ResponseStatusEnum.PARAMETER_ERROR);
        }
        executor.submitTask(() -> {
            log.info("开始执行文章查重,用户id:{}",userId);
            Date checkStartTime = new Date();
            try {
                List<ChannelPost> postList = channelPostService.getBaseMapper().selectBatchIds(dto.getPostIdList());
                if (CollectionUtils.isEmpty(postList)) {
                    return;
                }
                List<PostCheckInfoDto> infoList = postList.stream().map(e -> {
                    PostCheckInfoDto postCheckInfoDto = new PostCheckInfoDto();
                    postCheckInfoDto.setId(e.getPostId());
                    if (StringUtils.isNotBlank(e.getPostTitle())) {
                        postCheckInfoDto.setCheckTitle(e.getPostTitle());
                    } else {
                        if (StringUtils.length(e.getPostContent()) <= 20) {
                            postCheckInfoDto.setCheckTitle(e.getPostContent());
                        }else{
                            postCheckInfoDto.setCheckTitle(e.getPostContent().substring(0, 19) + "...");
                        }
                    }
                    return postCheckInfoDto;
                }).collect(Collectors.toList());
                Map<Integer, String> infoMap = infoList.stream()
                        .collect(Collectors.toMap(PostCheckInfoDto::getId, PostCheckInfoDto::getCheckTitle));
                stringRedisTemplate.opsForValue().set(RedisContants.ARTICLE_CHECK_LIST + userId,objectMapper.writeValueAsString(infoList));
                for (ChannelPost post : postList) {
                    CompletableFuture<JSONObject> weiboResponse = CompletableFuture.supplyAsync(() -> WlSimilarUtil.getSingleWebResult(null, post.getPostContent(), WlSimilarClassContent.INNER_URL_WEIBO),
                            executor.getOriginalExecutor());
                    CompletableFuture.allOf( weiboResponse).get();
                    ArticleCheckResult weiboResult = weiboResponse.get().toBean(ArticleCheckResult.class);
                    log.info("获取文章查重响应,weibo:{}",weiboResult.toString());
                    List<PostCheckScope> checkScopeList = new LinkedList<>();
                    checkScopeList.add(articleToPost(weiboResult, ArticleCheckPlatformEnum.WEIBO));
                    checkScopeList.sort(Comparator.comparing(PostCheckScope::getRepetitionRate).reversed());
                    PostCheck postCheck = PostCheck.builder()
                            .reportNo(createReportNo())
                            .checkTitle(infoMap.get(post.getPostId()))
                            .nickname(RandomUtil.randomString(3))
                            .userId(userId)
                            .postId(post.getPostId())
                            .checkStatus(PostCheckStatusEnum.COMPLETE.name())
                            .checkWordNum(getCharCount(post.getPostContent()))
                            .checkTime(checkStartTime)
                            .createTime(new Date())
                            .updateTime(new Date())
                            .completeTime(new Date())
                            .checkRate(checkScopeList.get(0).getRepetitionRate())
                            .build();
                    postCheckService.save(postCheck);
                    for (PostCheckScope scope : checkScopeList) {
                        scope.setPostCheckId(postCheck.getId());
                    }
                    postCheckScopeService.saveBatch(checkScopeList);
                }
                stringRedisTemplate.delete(RedisContants.ARTICLE_CHECK_LIST + userId);
            } catch (Exception e) {
                log.error("文章查重错误:",e);
            }
        });
    }

    @Override
    public ArticleCheckVo reportDetails(PostReportDto dto, Integer userId) {
        ArticleCheckVo result = new ArticleCheckVo();
        List<PostCheck> postCheckList = postCheckService.list(Wrappers.<PostCheck>lambdaQuery()
                .eq(PostCheck::getId,dto.getPostCheckId())
                .eq(PostCheck::getUserId,userId));
        if (CollectionUtils.isEmpty(postCheckList)) {
            return null;
        }
        BeanUtils.copyProperties(postCheckList.get(0), result);
        if (result.getCheckRate() != null) {
            result.setWriteRate(new BigDecimal("100").subtract(result.getCheckRate()));
        }
        List<PostCheckScope> checkScopeList = postCheckScopeService.list(Wrappers.<PostCheckScope>lambdaQuery()
                .eq(PostCheckScope::getPostCheckId,result.getId()));
        if (CollectionUtils.isEmpty(checkScopeList)) {
            return result;
        }
        result.setPostCheckScopeList(checkScopeList.stream().map(e -> {
            ArticleCheckVo.PostCheckScopeVo vo = new ArticleCheckVo.PostCheckScopeVo();
            BeanUtils.copyProperties(e, vo);
            return vo;
        }).collect(Collectors.toList()));
        return result;
    }

    @Override
    public ArticleCheckListVo list(Integer userId) {
        ArticleCheckListVo result = new ArticleCheckListVo();
        List<PostCheck> postCheckList = postCheckService.list(Wrappers.<PostCheck>lambdaQuery()
                .eq(PostCheck::getUserId,userId).orderByDesc(PostCheck::getCreateTime));
        if (CollectionUtils.isEmpty(postCheckList)) {
            return result;
        }
        result.setCompleteList(postCheckList.stream()
                .map(e -> {
                    ArticleCheckListVo.PostCheckScopeVo vo = new ArticleCheckListVo.PostCheckScopeVo();
                    BeanUtils.copyProperties(e, vo);
                    vo.setPostCheckId(e.getId());
                    return vo;
                }).collect(Collectors.toList()));
        String proceedListStr = stringRedisTemplate.opsForValue().get(RedisContants.ARTICLE_CHECK_LIST + userId);
        if (StringUtils.isBlank(proceedListStr)) {
            return result;
        }
        try {
            List<PostCheckInfoDto> infoList =  objectMapper.readValue(proceedListStr,new TypeReference<List<PostCheckInfoDto>>(){});
            List<ArticleCheckListVo.PostCheckScopeVo> voList = infoList.stream()
                    .map(e -> {
                        ArticleCheckListVo.PostCheckScopeVo vo = new ArticleCheckListVo.PostCheckScopeVo();
                        vo.setCheckTitle(e.getCheckTitle());
                        return vo;
                    }).collect(Collectors.toList());
            result.setProceedList(voList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    private PostCheckScope articleToPost(ArticleCheckResult article, ArticleCheckPlatformEnum platform) {
        return PostCheckScope.builder()
                .repeatContent(article.getLinkTitle())
                .repetitionRate(article.getSimilarRate() == null ? BigDecimal.ZERO : BigDecimal.valueOf(article.getSimilarRate()))
                .platform(platform.getCode())
                .articleLink(article.getLink())
                .createTime(new Date())
                .updateTime(new Date())
                .build();
    }

    private String createReportNo() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return LocalDateTime.now().format(formatter) + RandomStringUtils.randomNumeric(3);
    }

    private Integer getCharCount(String content) {
        content = content.replace(" ", "");
        content = content.replaceAll("[\\pP\\p{Punct}]", "");
        return content.length();
    }
}
